#define _CRT_SECURE_NO_WARNINGS
#include"PageCache.h"
PageCache PageCache::_sInst;

// 获取?个K?的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	//当申请页数大于128，找堆要
	if (k>NPAGES-1)
	{
		void* ptr = SystemAlloc(k);
		//Span* span = new Span;
		Span* span = spanPool.New();
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;
		_idSpanMap[span->_pageId] = span;
		//_idSpanMap.set(span->_pageId, span);


		//// 为所有页建立映射
		//for (PAGE_ID i = 0; i < k; ++i) {
		//	_idSpanMap[span->_pageId + i] = span;
		//}


		return span;
	}
	//检查第K个桶是否为空
	if (!_spanLists[k].Empty())
	{
		Span*kspan= _spanLists[k].PopFront();
		// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0;i < kspan->_n; ++i)
		{
			_idSpanMap[kspan->_pageId + i] = kspan;
			//_idSpanMap.set(kspan->_pageId + i, kspan);
		}
		return kspan;
	}
	//检查后面的桶是否有span，有的话切分
	for (int i = k + 1;i < NPAGES;i++)
	{
		if (!_spanLists[i].Empty())
		{
			//进行切分
			Span* nspan = _spanLists[i].PopFront();
			//Span* kspan = new Span;
			Span* kspan = spanPool.New();
			kspan->_pageId = nspan->_pageId;
			kspan->_n = k;
			nspan->_pageId += k;
			nspan->_n -= k;
			//k页返回，剩余的重新映射位置并插入
			_spanLists[nspan->_n].PushFront(nspan);
			//存储nspan得首尾页号与nspan得映射，方便pagecache回收内存时进行查找
			_idSpanMap[nspan->_pageId] = nspan;
			//_idSpanMap.set(nspan->_pageId, nspan);
			_idSpanMap[nspan->_pageId + nspan->_n - 1] = nspan;
			//_idSpanMap.set(nspan->_pageId + nspan->_n - 1, nspan);
			// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
			for (PAGE_ID i = 0;i < kspan->_n; ++i)
			{
				_idSpanMap[kspan->_pageId + i] = kspan;
				//_idSpanMap.set(kspan->_pageId + i, kspan);
			}
			return kspan;
		}
	}
	
	//Spanlist为空或空间不足
	//找堆要128
	//Span* NewBigSpan = new Span;
	Span* NewBigSpan = spanPool.New();
	void* ptr = SystemAlloc(NPAGES - 1);
	NewBigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	NewBigSpan->_n = NPAGES - 1;
	_spanLists[NewBigSpan->_n].PushFront(NewBigSpan);

	return NewSpan(k);
}

//获取从对象到span的映射
Span* PageCache::MapObjectTospan(void* obj)
{
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);
	//枷锁访问
	std::unique_lock<std::mutex>lock(_pageMtx);
	
	auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end())
	{
		return ret->second;
	}
	else {
		assert(false);
		return nullptr;
	}
	/*auto ret=(Span*)_idSpanMap.get(id);
	assert(ret != nullptr);
	return ret;*/
}

//释放空闲span回到Pagecache，并合并相邻的span
void PageCache::ReleaseSpanToPagecache(Span* span)
{	
	//大于128页得直接还给堆
	if (span->_n > NPAGES - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		//// 移除所有页的映射
		//for (PAGE_ID i = 0; i < span->_n; ++i) {
		//	_idSpanMap.erase(span->_pageId + i);
		//}
		SystemFree(ptr);
		spanPool.Delete(span);
		//delete span;
		return;
	}
	//向前合并
	while (1)
	{
		PAGE_ID preid = span->_pageId - 1;
		auto ret = _idSpanMap.find(preid);
		//前面页号不存在，不合并
		if (ret == _idSpanMap.end())
		{
			break;
		}
		
		/*auto ret = (Span*)_idSpanMap.get(preid);
		if (ret == nullptr)
		{
			break;
		}*/
		//Span* prespan = ret;
		Span* prespan = ret->second;
		//前面页号被使用，不合并
		if (prespan->_isuse == true)
		{
			break;
		}
		//两页内存之和超过上限，不合并
		if (prespan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		span->_pageId = prespan->_pageId;
		span->_n += prespan->_n;
		_spanLists[prespan->_n].Erase(prespan);
		spanPool.Delete(prespan);
		//delete prespan;
	}
	//向后合并
	while (1)
	{
		PAGE_ID nextid = span->_pageId +span->_n;
		auto ret = _idSpanMap.find(nextid);
		//后面页号不存在，不合并
		if (ret == _idSpanMap.end())
		{
			break;
		}
		/*auto ret = (Span*)_idSpanMap.get(nextid);
		if (ret == nullptr)
		{
			break;
		}*/

		//Span* nextspan = ret;
		Span* nextspan = ret->second;
		//后面页号被使用，不合并
		if (nextspan->_isuse == true)
		{
			break;
		}
		//两页内存之和超过上限，不合并
		if (nextspan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		span->_n+= nextspan->_n;
		_spanLists[nextspan->_n].Erase(nextspan);
		spanPool.Delete(nextspan);
		//delete nextspan;
	}
	_spanLists[span->_n].PushFront(span);
	span->_isuse = false;
	_idSpanMap[span->_pageId] = span;
	_idSpanMap[span->_pageId + span->_n - 1] = span;
	//_idSpanMap.set(span->_pageId, span);
	//_idSpanMap.set(span->_pageId + span->_n - 1, span);
}
